昨天練習測試專案預設的元件後,今天要來測試之前所開發的Todolist app
來回憶一下Todolist的樣子
這個Todolist我們要測試的情境有以下6個:
那我們就開始吧
新增Nunit測試專案並將bUnit套件安裝好,新增TodoTests類別
[Test]
public void 顯示待辦清單且有2筆待辦事項()
{
//arrange
var ctx = new Bunit.TestContext();
//act
var cut = ctx.RenderComponent<Index>();
//assert
string expectedHtml = @"<div>
<table class='table table-hover'>
<tbody>
<tr>
<th>待辦事項</th >
<th> 刪除 </th >
</tr >
<tr>
<td>Buy Milk</td>
<td>
<button class='btn btn-danger btn-sm' >delete</button>
</td>
</tr>
<tr>
<td>Buy Apple</td>
<td>
<button class='btn btn-danger btn-sm' >delete</button>
</td>
</tr>
</tbody>
</table>
</div>
<div>
<label>New Todo:</label>
<input type='text'>
<button class='btn btn-primary' id='AddBtn'>Add</button>
</div>
<br>";
//與預期產生的html做比對
cut.MarkupMatches(expectedHtml);
}
[Test]
public void 輸入框有值_點Add按鈕_待辦事項出現在待辦清單()
{
var ctx = new Bunit.TestContext();
var cut = ctx.RenderComponent<Index>();
//在New Todo的input輸入框,輸入New Item
cut.Find("input").Change("New Item");
//點擊Add按鈕
cut.Find("#AddBtn").Click();
//預期的待辦清單將會多一個New Item
string expectedHtml = @"<div>
<table class='table table-hover'>
<tbody>
<tr>
<th>待辦事項</th >
<th> 刪除 </th >
</tr >
<tr>
<td>Buy Milk</td>
<td>
<button class='btn btn-danger btn-sm' >delete</button>
</td>
</tr>
<tr>
<td>Buy Apple</td>
<td>
<button class='btn btn-danger btn-sm' >delete</button>
</td>
</tr>
<tr>
<td>New Item</td>
<td>
<button class='btn btn-danger btn-sm' >delete</button>
</td>
</tr>
</tbody>
</table>
</div>
<div>
<label>New Todo:</label>
<input type='text' value=''>
<button class='btn btn-primary' id='AddBtn'>Add</button>
</div>
<br>";
cut.MarkupMatches(expectedHtml);
}
輸入框沒有值這件事,可以再分成沒有輸入或輸入空白字串,因此同樣的測試程式碼,可以用NUnit框架的[TestCase]代入參數來進行:
[TestCase("")]
[TestCase(" ")]
public void 輸入框沒有值_點Add按鈕_待辦清單沒有新增待辦事項(string input)
{
var ctx = new Bunit.TestContext();
var cut = ctx.RenderComponent<Index>();
cut.Find("input").Change(input);
cut.Find("#AddBtn").Click();
string expectedHtml = @$"<div>
<table class='table table-hover'>
<tbody>
<tr>
<th>待辦事項</th >
<th> 刪除 </th >
</tr >
<tr>
<td>Buy Milk</td>
<td>
<button class='btn btn-danger btn-sm' >delete</button>
</td>
</tr>
<tr>
<td>Buy Apple</td>
<td>
<button class='btn btn-danger btn-sm' >delete</button>
</td>
</tr>
</tbody>
</table>
</div>
<div>
<label>New Todo:</label>
<input type='text' value='{input}' >
<button class='btn btn-primary' id='AddBtn'>Add</button>
</div>
<br>";
cut.MarkupMatches(expectedHtml);
}
執行測試之後,Oops! 空白字串的測試沒過
來確認一下元件的Add程式碼
void Add()
{
if (!string.IsNullOrEmpty(newTodoItem))
{
TodoList.Add(newTodoItem);
newTodoItem = "";
}
}
由於空白字串對於string.IsNullOrEmpty方法來說是false,所以符合條件進行Todolist的新增動作,因此我們可以將string.IsNullOrEmpty改為String.IsNullOrWhiteSpace,就可以通過測試囉
var ctx = new Bunit.TestContext();
var cut = ctx.RenderComponent<Index>();
cut.Find("input").Change("New Item");
cut.Find("#AddBtn").Click();
string expectedHtml = @"<div>
<table class='table table-hover'>
<tbody>
<tr>
<th>待辦事項</th >
<th> 刪除 </th >
</tr >
<tr>
<td>Buy Milk</td>
<td>
<button class='btn btn-danger btn-sm' >delete</button>
</td>
</tr>
<tr>
<td>Buy Apple</td>
<td>
<button class='btn btn-danger btn-sm' >delete</button>
</td>
</tr>
<tr>
<td>New Item</td>
<td>
<button class='btn btn-danger btn-sm' >delete</button>
</td>
</tr>
</tbody>
</table>
</div>
<div>
<label>New Todo:</label>
<input type='text' value=''>
<button class='btn btn-primary' id='AddBtn'>Add</button>
</div>
<br>";
cut.MarkupMatches(expectedHtml);
Todolist的刪除功能使用sweetalert這個套件,在元件中是使用IJSRuntime物件來進行javascript的呼叫,因此在測試程式碼這邊,也需要mock一個IJSRuntime物件,來設定sweetalert confirm的回傳值。
要mock IJSRuntime,可以用一般常看到的幾個知名mocking library,像是moq、NSubstitute等等,這邊我們就直接用bUnit中的mock,這邊我們就直接用bUnit中的AddMockJSRuntime來進行mock動作。
[Test]
public void 點擊刪除鈕並確定_可刪除待辦事項()
{
var ctx = new Bunit.TestContext();
//mock JSRuntime
var mockJS = ctx.Services.AddMockJSRuntime();
//設定SweetConfirm function回傳true
mockJS.Setup<bool>("SweetConfirm", "Delete", $"確定要刪除Buy Milk?").SetResult(true);
var cut = ctx.RenderComponent<Index>();
var firstDeleteButton = cut.FindAll("button").FirstOrDefault();
//點擊第一個刪除按鈕
firstDeleteButton.Click();
string expectedHtml = @"<div>
<table class='table table-hover'>
<tbody>
<tr>
<th>待辦事項</th>
<th>刪除</th>
</tr>
<tr>
<td>Buy Apple</td>
<td>
<button class='btn btn-danger btn-sm' >delete</button>
</td>
</tr>
</tbody>
</table>
</div>
<div>
<label>New Todo:</label>
<input type='text'>
<button class='btn btn-primary' id='AddBtn'>Add</button>
</div>
<br>";
cut.MarkupMatches(expectedHtml);
}
[Test]
public void 點擊刪除鈕但取消_不會刪除待辦事項()
{
var ctx = new Bunit.TestContext();
//mock JSRuntime
var mockJS = ctx.Services.AddMockJSRuntime();
//設定SweetConfirm function回傳false
mockJS.Setup<bool>("SweetConfirm", "Delete", $"確定要刪除Buy Milk?").SetResult(false);
var cut = ctx.RenderComponent<Index>();
var firstDeleteButton = cut.FindAll("button").FirstOrDefault();
firstDeleteButton.Click();
string expectedHtml = @"<div>
<table class='table table-hover'>
<tbody>
<tr>
<th>待辦事項</th>
<th> 刪除 </th>
</tr>
<tr>
<td>Buy Milk</td>
<td>
<button class='btn btn-danger btn-sm' >delete</button>
</td>
</tr>
<tr>
<td>Buy Apple</td>
<td>
<button class='btn btn-danger btn-sm' >delete</button>
</td>
</tr>
</tbody>
</table>
</div>
<div>
<label>New Todo:</label>
<input type='text'>
<button class='btn btn-primary' id='AddBtn'>Add</button>
</div>
<br>";
cut.MarkupMatches(expectedHtml);
}
測試都通過囉~